package org.bjf.modules.process.service;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.bjf.exception.ServiceException;
import org.bjf.modules.process.bean.ProcessDefInfo;
import org.bjf.modules.process.bean.TaskInfo;
import org.bjf.utils.SpringUtil;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.HistoryService;
import org.flowable.engine.IdentityService;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.idm.api.Group;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author bjf
 * @date 2017/11/27
 */
@Service
@Slf4j
public class ProcessService {

  @Autowired
  private ProcessEngine processEngine;
  @Autowired
  private RepositoryService repositoryService;
  @Autowired
  private RuntimeService runtimeService;
  @Autowired
  private TaskService taskService;
  @Autowired
  private IdentityService identityService;
  @Autowired
  private HistoryService historyService;


  /**
   * 发布流程
   *
   * @param resourceName 流程ID
   * @param is 流程文件输入流
   */
  public ProcessDefinitionQuery deploy(String resourceName, InputStream is) {
    /*if (!StringUtils.endsWithAny(resourceName, "bpmn", "bpmn20.xml")) {
      resourceName += "bpmn20.xml";
    }*/
    RepositoryService repositoryService = SpringUtil.getBean(RepositoryService.class);
    Deployment deploy = repositoryService.createDeployment().addInputStream(resourceName, is)
        .deploy();
    return repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId());
  }

  /**
   * 删除流程
   *
   * @param deployId 发布ID
   */
  public void deleteDeploy(String deployId) {
    repositoryService.deleteDeployment(deployId);

  }

  /**
   * 流程定义列表
   */
  public List<ProcessDefInfo> listProcessDef() {
    List<ProcessDefinition> defs = repositoryService.createProcessDefinitionQuery()
        .orderByDeploymentId().orderByDeploymentId().asc().list();

    //只取version最大的
    Map<String, ProcessDefinition> defMap = new HashMap<>(defs.size());
    for (ProcessDefinition def : defs) {
      defMap.put(def.getKey(), def);
    }

    List<ProcessDefInfo> list = new ArrayList<>(defMap.size());
    for (Entry<String, ProcessDefinition> entry : defMap.entrySet()) {
      ProcessDefinition definition = entry.getValue();

      ProcessDefInfo def = new ProcessDefInfo()
          .setDefId(definition.getId())
          .setDefKey(definition.getKey())
          .setDefName(definition.getName())
          .setDeployId(definition.getDeploymentId())
          .setCategory(definition.getCategory())
          .setDescription(definition.getDescription())
          .setVersion(definition.getVersion());

      list.add(def);
    }
    return list;
  }

  /**
   * 启动流程
   *
   * @param userId 用户ID
   * @param defKey 流程定义Key
   * @param vars 流程变量
   * @return 流程实例ID
   */
  public String startProcess(Long userId, String defKey, Map<String, Object> vars) {
    // 设置发起人
    identityService.setAuthenticatedUserId(userId + "");
    ProcessInstance pi = runtimeService.startProcessInstanceByKey(defKey, vars);
    return pi.getId();
  }

  /**
   * 待办列表
   */
  public List<TaskInfo> listTask(Long userId) {
    //===1.查出用户的流程列表
    List<Task> tasks = taskService.createTaskQuery().
        or().
        taskAssignee(String.valueOf(userId))
        .taskCandidateUser(String.valueOf(userId))
        .endOr()
        .orderByTaskCreateTime().desc()
        .list();

    //===2.查出当前登录用户所在的用户组
    List<Group> groups = identityService.createGroupQuery().groupMember(String.valueOf(userId))
        .list();
    if (!groups.isEmpty()) {
      //===3.查询用户组的假流程列表
      List<String> groupIds = groups.stream().map(Group::getId).collect(Collectors.toList());
      List<Task> groupTasks = taskService.createTaskQuery().taskCandidateGroupIn(groupIds).list();
      tasks.addAll(groupTasks);
    }

    List<TaskInfo> list = new ArrayList<>(tasks.size());
    for (Task task : tasks) {
      TaskInfo info = new TaskInfo()
          .setTaskId(task.getId())
          .setTaskName(task.getName())
          .setProcessInstanceId(task.getProcessInstanceId())
          .setProcessDefinitionId(task.getProcessDefinitionId())
          .setVariables(taskService.getVariables(task.getId()))
          .setAssignee(task.getAssignee())
          .setCategory(task.getCategory())
          .setOwner(task.getOwner())
          .setDescription(task.getDescription())
          .setCreateTime(task.getCreateTime());

      list.add(info);
    }

    return list;
  }


  /**
   * 审批
   *
   * @param taskId 任务ID
   * @param vars 流程变量
   */
  public void submitTask(String taskId, Map<String, Object> vars) {
    //查询当前审批节点
    Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
    if (task == null) {
      throw new ServiceException("流程任务不存在");
    }

    //完成审批任务
    taskService.complete(task.getId(), vars);
  }

  /**
   * 历史任务列表
   *
   * @param processInstanceId 流程实例ID
   */
  public List<TaskInfo> listHisTask(String processInstanceId) {
    List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
        .processInstanceId(processInstanceId).orderByTaskCreateTime().desc().list();
    List<TaskInfo> tasks = new ArrayList<>(list.size());

    for (HistoricTaskInstance task : list) {
      TaskInfo info = new TaskInfo()
          .setTaskId(task.getId())
          .setTaskName(task.getName())
          .setProcessInstanceId(task.getProcessInstanceId())
          .setProcessDefinitionId(task.getProcessDefinitionId())
          .setVariables(task.getProcessVariables())
          .setAssignee(task.getAssignee())
          .setCategory(task.getCategory())
          .setOwner(task.getOwner())
          .setDescription(task.getDescription())
          .setCreateTime(task.getCreateTime());

      tasks.add(info);
    }

    return tasks;
  }


  /**
   * 获取流程图
   */
  public InputStream getProcessImage(String processInstanceId) {
    //获得流程实例
    ProcessInstance pi = runtimeService.createProcessInstanceQuery()
        .processInstanceId(processInstanceId).singleResult();
    String processDefinitionId;
    if (pi == null) {
      //查询已经结束的流程实例
      HistoricProcessInstance hisPi = historyService.createHistoricProcessInstanceQuery()
          .processInstanceId(processInstanceId).singleResult();
      if (hisPi == null) {
        throw new ServiceException("流程不存在");
      }
      processDefinitionId = hisPi.getProcessDefinitionId();

    } else {
      processDefinitionId = pi.getProcessDefinitionId();
    }

    ///使用宋体
    String fontName = "宋体";
    //获取BPMN模型对象
    BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
    //获取流程实例当前的节点，需要高亮显示
    List<String> currentActs = Collections.emptyList();
    List<String> flows = Collections.emptyList();
    if (pi != null) {
      currentActs = runtimeService.getActiveActivityIds(pi.getId());
    }
    return processEngine.getProcessEngineConfiguration()
        .getProcessDiagramGenerator()
        .generateDiagram(model, "png", currentActs, flows, fontName, fontName, fontName, null, 1.0,
            Boolean.TRUE);
  }

  /**
   * 获取流程图
   */
  public InputStream getProcessImageByDef(String processDefinitionId) {

    //获取BPMN模型对象
    BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
    List<String> currentActs = Collections.emptyList();
    List<String> flows = Collections.emptyList();
    ///使用宋体
    String fontName = "宋体";
    return processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator()
        .generateDiagram(model, "png", currentActs, flows, fontName, fontName, fontName, null, 1.0,
            Boolean.TRUE);
  }


}
